Get values from custom packet in IP option fields in scapy - python-3.x

I want to get the value of some fields in my scapy code for receiving packets, but I don't know how to do it exactly. When I print the value, I get the error that the layer is not defined or AttributeError: 'NoneType' object has no attribute 'proto'
class TELEMETRY(IPOption):
name = "TELEMETRY"
option = 31
fields_desc = [ _IPOption_HDR,
ByteField("length", 2),
Emph(SourceIPField("src", "dst")),
Emph(DestIPField("dst", "127.0.0.1")),
ShortEnumField("sport", 20, TCP_SERVICES),
ShortEnumField("dport", 80, TCP_SERVICES),
ByteEnumField("proto", 0, IP_PROTOS),
BitField("timeTaken", 0, 32),
BitField("egress_timestamp", 0, 48),
BitField("enqQdepth", 0, 19),
BitField("deqQdepth", 0, 19),
BitField("padding", 0, 2) ]
I can access the IP packet within the code blow but when I want to access the telemetry fields in my custom fields,
I get error AttributeError: 'NoneType' object has no attribute 'proto'
def handle_pkt(pkt):
ip_src=pkt[IP].src
ip_dst=pkt[IP].dst
ip_ver=pkt[IP].version
ip_id=pkt[IP].id
telemetry = pkt.getlayer(TELEMETRY)
print ip_src,ip_dst,ip_ver,ip_id, telemetry.proto
os.system(" echo %s %s %s %s %s| nc localhost 6666" % (ip_src,ip_dst,ip_ver,ip_id,telemetry.proto))
Here is the result of pkt.show2()
niffing on h4-eth0
got a packet
###[ Ethernet ]###
dst = 08:00:00:00:02:00
src = ff:ff:ff:ff:ff:ff
type = IPv4
###[ IP ]###
version = 4
ihl = 13
tos = 0x0
len = 172
id = 1
flags =
frag = 0
ttl = 63
proto = tcp
chksum = 0x5efb
src = 192.168.1.1
dst = 192.168.3.3
\options \
|###[ TELEMETRY ]###
| copy_flag = 0
| optclass = control
| option = 31
| length = 32
| src = 192.168.1.1
| dst = 192.168.3.3
| sport = 64314
| dport = 1234
| proto = tcp
| timeTaken = 11
| egress_timestamp= 7740314797
| enqQdepth = 0
| deqQdepth = 0
| padding = 0
###[ TCP ]###
sport = 64314
dport = 1234
seq = 0
ack = 0
dataofs = 5
reserved = 0
flags = S
window = 8192
chksum = 0x2d40
urgptr = 0
options = ''
Any idea would be appreciated. :)

getlayer
works when you want to get a sub layer.
In you case, you want to get a layer inside a list.
(IP.options is a list of layers)
the solution is then:
telemetry = pkt[IP].options[0]
now, for the option list might be empty for a variaty of reason, so you might want to:
if len(pkt[IP].options):
telemetry = pkt[IP].options[0]
else:
pass
# deal with it
Carcigenicate Also pointed out that you might have more of those options in your packet, you may want to deal with that too.

Related

When tracing shared library functions with funclatency, no function names were displayed

When using the bcc tool funclatency, there are unknown function names.
It would be helpful if I could track the entry and return values of a number of functions contained in the library ibverbs (Infiniband).
I use funclatency to print a histogram of the ibverbs functions called by perftest.
https://github.com/iovisor/bcc/tree/master/tools
To send packages between two nodes, I use the perftest.
https://github.com/linux-rdma/perftest
To compile the perftest application, I used the following compiler flags:
CFLAGS = -g -Wall -D_GNU_SOURCE -O3 -ggdb3 -O2 -fno-omit-frame-pointer
System:
Distributor ID: Debian
Description: Debian GNU/Linux 11 (bullseye)
Release: 11
Codename: bullseye
I use funclatency like this:
sudo funclatency-bpfcc ibverbs:ibv_get*
funclatency output:
Tracing 13 functions for "ibverbs:ibv_get*"... Hit Ctrl-C to end.
Function = b'[unknown]' [784402]
nsecs : count distribution
0 -> 1 : 0 | |
2 -> 3 : 0 | |
4 -> 7 : 0 | |
8 -> 15 : 0 | |
16 -> 31 : 0 | |
32 -> 63 : 0 | |
64 -> 127 : 0 | |
128 -> 255 : 0 | |
256 -> 511 : 0 | |
512 -> 1023 : 0 | |
1024 -> 2047 : 468 |**************************** |
2048 -> 4095 : 512 |****************************** |
4096 -> 8191 : 664 |****************************************|
8192 -> 16383 : 332 |******************** |
16384 -> 32767 : 14 | |
32768 -> 65535 : 3 | |
65536 -> 131071 : 6 | |
131072 -> 262143 : 0 | |
262144 -> 524287 : 0 | |
524288 -> 1048575 : 0 | |
1048576 -> 2097151 : 0 | |
2097152 -> 4194303 : 0 | |
4194304 -> 8388607 : 0 | |
8388608 -> 16777215 : 1 | |
Here is the source code for the translation of a memory address into a function name:
https://github.com/iovisor/bcc/blob/master/src/python/bcc/init.py
def sym(addr, pid, show_module=False, show_offset=False, demangle=True):
"""sym(addr, pid, show_module=False, show_offset=False)
Translate a memory address into a function name for a pid, which is
returned. When show_module is True, the module name is also included.
When show_offset is True, the instruction offset as a hexadecimal
number is also included in the string.
A pid of less than zero will access the kernel symbol cache.
Example output when both show_module and show_offset are True:
"start_thread+0x202 [libpthread-2.24.so]"
Example output when both show_module and show_offset are False:
"start_thread"
"""
#addr is of type stacktrace_build_id
#so invoke the bsym address resolver
typeofaddr = str(type(addr))
if typeofaddr.find('bpf_stack_build_id') != -1:
sym = bcc_symbol()
b = bcc_stacktrace_build_id()
b.status = addr.status
b.build_id = addr.build_id
b.u.offset = addr.offset
res = lib.bcc_buildsymcache_resolve(BPF._bsymcache,
ct.byref(b),
ct.byref(sym))
if res < 0:
if sym.module and sym.offset:
name,offset,module = (None, sym.offset,
ct.cast(sym.module, ct.c_char_p).value)
else:
name, offset, module = (None, addr, None)
else:
name, offset, module = (sym.name, sym.offset,
ct.cast(sym.module, ct.c_char_p).value)
else:
name, offset, module = BPF._sym_cache(pid).resolve(addr, demangle)
offset = b"+0x%x" % offset if show_offset and name is not None else b""
name = name or b"[unknown]"
name = name + offset
module = b" [%s]" % os.path.basename(module) \
if show_module and module is not None else b""
return name + module
How can I read the function name correctly? It should not just return b'[unknown]'!

generator do not get stored in memory then how come i get those value even after the loop is ended

A loop is created for generating a series of number
import ctypes
g = [ ]
for item1 in range(10):
print(f'memory address of {item1} = {id(item1)}')
g.append(id(item1))
here my loops end but the only thing is stored is the location or the memory address not the number
checking the values at that memory address
for item in g:
a = ctypes.cast(item, ctypes.py_object).value
print(f'values at that memory address = {a}')
here
a = ctypes.cast(item, ctypes.py_object).value
gives the value stored at that memory address
output
C:\Users\Admin\Desktop\pythonProject\venv\Scripts\python.exe C:/Users/Admin/Desktop/pythonProject/main.py
memory address of 0 = 140709883771936
memory address of 1 = 140709883771968
memory address of 2 = 140709883772000
memory address of 3 = 140709883772032
memory address of 4 = 140709883772064
memory address of 5 = 140709883772096
memory address of 6 = 140709883772128
memory address of 7 = 140709883772160
memory address of 8 = 140709883772192
memory address of 9 = 140709883772224
my loop has ended before soo they are no longer in the memory according to generator but still those memory address shows the values of those elements of previous loop
values at that memory address = 0
values at that memory address = 1
values at that memory address = 2
values at that memory address = 3
values at that memory address = 4
values at that memory address = 5
values at that memory address = 6
values at that memory address = 7
values at that memory address = 8
values at that memory address = 9
Process finished with exit code 0
This is just an implementation detail of cpython which has prebuilt singletons for the numbers -4 through 256. Python holds its own reference to these numbers. Your loop adds and removes a reference, but that original reference remains, keeping the values in the same place in memory.
If you choose a range outside of this sequence, you get a different result.
import ctypes
g = [ ]
for item1 in range(257, 267):
print(f'memory address of {item1} = {id(item1)}')
g.append(id(item1))
for item in g:
a = ctypes.cast(item, ctypes.py_object).value
print(f'values at that memory address = {a}')
Output
memory address of 257 = 139740170741360
memory address of 258 = 139740170743920
memory address of 259 = 139740170743952
memory address of 260 = 139740170743856
memory address of 261 = 139740170743824
memory address of 262 = 139740170741744
memory address of 263 = 139740170743792
memory address of 264 = 139740170744048
memory address of 265 = 139740170744080
memory address of 266 = 139740170744112
values at that memory address = 139740170743920
values at that memory address = 139740170743952
values at that memory address = 139740170743856
values at that memory address = 139740170743824
values at that memory address = 139740170741744
values at that memory address = 139740170743792
values at that memory address = 139740170744048
values at that memory address = 139740170744080
values at that memory address = 139740170744112
values at that memory address = 266
A range is not a generator It is a class of a series of positive integers int inherited from the base class object in Python, and hence it is not a function or a generator. but if you goo through it's class code you can see __iter__ method, from which you can say that, it has the functionality of iteration and for loop underneat the hud uses iter() which is iteration. hence it should be able to iterate range
code
help(range)
output
C:\Users\Admin\PycharmProjects\pythonProject1\venv\Scripts\python.exe C:/Users/Admin/PycharmProjects/pythonProject1/main.py
Help on class range in module builtins:
class range(object)
| range(stop) -> range object
| range(start, stop[, step]) -> range object
|
| Return an object that produces a sequence of integers from start (inclusive)
| to stop (exclusive) by step. range(i, j) produces i, i+1, i+2, ..., j-1.
| start defaults to 0, and stop is omitted! range(4) produces 0, 1, 2, 3.
| These are exactly the valid indices for a list of 4 elements.
| When step is given, it specifies the increment (or decrement).
|
| Methods defined here:
|
| __bool__(self, /)
| self != 0
|
| __contains__(self, key, /)
| Return key in self.
|
| __eq__(self, value, /)
| Return self==value.
|
| __ge__(self, value, /)
| Return self>=value.
|
| __getattribute__(self, name, /)
| Return getattr(self, name).
|
| __getitem__(self, key, /)
| Return self[key].
|
| __gt__(self, value, /)
| Return self>value.
|
| __hash__(self, /)
| Return hash(self).
|
| __iter__(self, /)
| Implement iter(self).
|
| __le__(self, value, /)
| Return self<=value.
|
| __len__(self, /)
| Return len(self).
|
| __lt__(self, value, /)
| Return self<value.
|
| __ne__(self, value, /)
| Return self!=value.
|
| __reduce__(...)
| Helper for pickle.
|
| __repr__(self, /)
| Return repr(self).
|
| __reversed__(...)
| Return a reverse iterator.
|
| count(...)
| rangeobject.count(value) -> integer -- return number of occurrences of value
|
| index(...)
| rangeobject.index(value) -> integer -- return index of value.
| Raise ValueError if the value is not present.
|
| ----------------------------------------------------------------------
| Static methods defined here:
|
| __new__(*args, **kwargs) from builtins.type
| Create and return a new object. See help(type) for accurate signature.
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| start
|
| step
|
| stop
Process finished with exit code 0

What are these mysterious fields in scapy?

import scapy.all as scapy
pkt = scapy.IP(src='127.0.0.1', dst='127.0.0.1')
pkt.show2()
result:
###[ IP ]###
version = 4
ihl = 5
tos = 0x0
len = 20
id = 1
flags =
frag = 0
ttl = 64
proto = ip
chksum = 0x7ce7
src = 127.0.0.1
dst = 127.0.0.1
\options \
What is chksum in scapy do i need care about it ?
what is ihl = 5 ?
what is len = 20 ?
i have searched on google about checksum but i don't know if it is related to the chksum of scapy i am not sure anyone can help ?

Change IP in ICMP scapy

I have this ICMP packet and I want to change IP in ICMP field (I've also tried to understand what this inner IP header is), how do I access it?
I've tried things like pack[ICMP].fieldname but nothing works so far.
###[ IP ]###
version = 4
ihl = 5
tos = 0x0
len = 84
id = 2531
flags = DF
frag = 0
ttl = 63
proto = icmp
chksum = 0x1ab3
src = 192.168.100.2
dst = 192.168.100.1
\options \
###[ ICMP ]###
type = dest-unreach
code = fragmentation-needed
chksum = 0xfcfb
reserved = 0
length = 0
nexthopmtu= 0
unused = ''
###[ IP in ICMP ]###
version = 9
ihl = 2
tos = 0x5f
len = 59487
id = 0
flags =
frag = 0
ttl = 231
proto = esp
chksum = 0x800
src = 0.0.0.0
dst = 16.17.18.19
\options \
|###[ IP Option Router Alert ]###
| copy_flag = 0
| optclass = control
| option = router_alert
| length = 21
| alert = 5655
|###[ IP Option ]###
| copy_flag = 0
| optclass = control
| option = upstream_multicast_packet
| length = 25
| value = '\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+'
Nevermind I've found the answer: an ICMP() error incapsulates his original IP header, so it's IP/ICMP/IP
IPerror is the field that permits to change it.

Why Scapy recalculates fragmentation size?

I am trying to fragment 120 bytes IP payload by 100 bytes. However, in output I got two packets one with 138 bytes and other with 50 bytes (Ethernet and IP header size are 14 and 20 bytes respectively). In first packet data offset starts from 0 to 103 and for second packet data offset starts from 104 to 119. Firstly I cannot understand why it works in this way. In order to understand I tried to look to source of defined fragment function in layers/inet.py line 552.
Scapy recalculates fragmentation size as follows:
def fragment(self, fragsize=1480):
"""Fragment IP datagrams"""
fragsize = (fragsize + 7) // 8 * 8 # <- RECALCULATION OF FRAGMENT SIZE
lst = []
fnb = 0
fl = self
while fl.underlayer is not None:
fnb += 1
fl = fl.underlayer
for p in fl:
s = raw(p[fnb].payload)
nb = (len(s) + fragsize - 1) // fragsize
for i in range(nb):
q = p.copy()
del(q[fnb].payload)
del(q[fnb].chksum)
del(q[fnb].len)
if i != nb - 1:
q[fnb].flags |= 1
q[fnb].frag += i * fragsize // 8
r = conf.raw_layer(load=s[i * fragsize:(i + 1) * fragsize])
r.overload_fields = p[fnb].payload.overload_fields.copy()
q.add_payload(r)
lst.append(q)
return lst
Can somebody explain why it is doing so?
N.B:
Ethernet header size 14 byte
IPv4 header size 20 byte
See https://github.com/secdev/scapy/issues/2424#issuecomment-576879663
From https://www.rfc-editor.org/rfc/rfc791#section-3.2 (page 25, top):
If an internet datagram is fragmented, its data portion must be broken on 8 octet boundaries.
To answer your question, fragment size must be a multiple of 8.
104 is a multiple of 8, not 100

Resources