Skip to content

contrib: provide support for MQTT-SN#2131

Merged
gpotter2 merged 8 commits intosecdev:masterfrom
miri64:mqttsn_layer
Jul 16, 2019
Merged

contrib: provide support for MQTT-SN#2131
gpotter2 merged 8 commits intosecdev:masterfrom
miri64:mqttsn_layer

Conversation

@miri64
Copy link
Copy Markdown
Contributor

@miri64 miri64 commented Jul 5, 2019

MQTT-SN is a sister protocol to MQTT that allows for MQTT-like traffic over UDP. It was designed to be as close as possible to MQTT but has some differences in its header format, so the MQTT layer of scapy can't be used to handle MQTT-SN packets.

@miri64
Copy link
Copy Markdown
Contributor Author

miri64 commented Jul 5, 2019

I did some testing locally, but did not integrate with your test framework yet. I will provide tests for that ASAP.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This field also should manipulate the adjust of the len field of the MQTTSN wrapping packet, as the len for this packet type only signifies the length of the MQTTSNEncaps header without the encapsulated message (see section 5.5 of the spec). Is there a way to express this?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't you use pkt.underlayer in the adjust ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the adjust of this field you mean?

Copy link
Copy Markdown
Contributor Author

@miri64 miri64 Jul 5, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I see this correctly, StrLenField does not have an adjust parameter in its constructor.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, I meant

the adjust of the len field of the MQTTSN wrapping packet

It might be better if you clarify 😄 from what understand len in MQTTSN needs info from w_node_id. It can get this info during the adjust of the len

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be better if you clarify 😄

Ok, I'll try my best 😅:

For all packet types except this one the len field of MQTTSN (the underlayer of these packet types) denotes the total length of the packet. Only for this it denotes the length until the end of w_node_id (called "wireless node id" in the spec), and the length of the remainder is denoted by len field of a following MQTTSN packet (the encapsulated MQTT-SN datagram). So only for this I need to manipulate the len field of MQTTSN somehow to only go to the end of w_node_id string.

from what understand len in MQTTSN needs info from w_node_id. It can get this info during the adjust of the len

Do you mean something like this?

class MQTTSN(Packet):
    name = "MQTT-SN header"
    fields_desc = [
        # …
        VariableFieldLenField("len", None, length_of="len",
                              adjust=lambda pkt, x: x + len(pkt.payload))
                              if type(pkt.payload) is not MQTTSNEncaps
                              else x + len(pkt.payload.w_node_id) + 1,
        # …
    ]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I somewhat did it like that :-)

@codecov
Copy link
Copy Markdown

codecov bot commented Jul 5, 2019

Codecov Report

Merging #2131 into master will increase coverage by 0.07%.
The diff coverage is 100%.

@@            Coverage Diff             @@
##           master    #2131      +/-   ##
==========================================
+ Coverage   87.25%   87.33%   +0.07%     
==========================================
  Files         198      200       +2     
  Lines       44681    45384     +703     
==========================================
+ Hits        38986    39635     +649     
- Misses       5695     5749      +54
Impacted Files Coverage Δ
scapy/contrib/mqttsn.py 100% <100%> (ø)
scapy/pton_ntop.py 89.04% <0%> (-8.22%) ⬇️
scapy/sessions.py 95.9% <0%> (-1.93%) ⬇️
scapy/utils.py 81.39% <0%> (-0.63%) ⬇️
scapy/arch/bpf/supersocket.py 77.77% <0%> (-0.37%) ⬇️
scapy/sendrecv.py 84.09% <0%> (-0.18%) ⬇️
scapy/arch/windows/__init__.py 71.81% <0%> (-0.16%) ⬇️
scapy/layers/inet6.py 88.1% <0%> (-0.12%) ⬇️
scapy/compat.py 100% <0%> (ø) ⬆️
scapy/error.py 100% <0%> (ø) ⬆️
... and 13 more

@miri64
Copy link
Copy Markdown
Contributor Author

miri64 commented Jul 5, 2019

Fixed some errors I found during writing of the tests and provided tests.

@miri64
Copy link
Copy Markdown
Contributor Author

miri64 commented Jul 5, 2019

Let me know if I should squash, the checklist said, I should but the changes I fixed-up were done after I opened the PR, and I know from experience that the preferences of each reviewer can be different ;-).

Copy link
Copy Markdown
Member

@gpotter2 gpotter2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's looking good.
Could you add a test that dissects a full packet, so that it tests the bindings with UDP ?

@miri64
Copy link
Copy Markdown
Contributor Author

miri64 commented Jul 5, 2019

Could you add a test that dissects a full packet, so that it tests the bindings with UDP ?

Will do

@miri64
Copy link
Copy Markdown
Contributor Author

miri64 commented Jul 5, 2019

And done. I used some dumps from some experiments of mine. The IPv6 prefix is an example prefix (2001:db8::/32) and the IIDs of the address are only locally valid (generated from the MCU's serial number).

@miri64
Copy link
Copy Markdown
Contributor Author

miri64 commented Jul 6, 2019

Fixed errors pointed out by travis.

miri64 added 2 commits July 6, 2019 16:14
MQTT-SN is a sister protocol to MQTT that allows for MQTT-like traffic
over UDP. It was designed to be as close as possible to MQTT but has
some differences in its header format, so the MQTT layer of `scapy`
can't be used to handle MQTT-SN packets.
@miri64
Copy link
Copy Markdown
Contributor Author

miri64 commented Jul 6, 2019

And squashed

miri64 added 3 commits July 9, 2019 14:50
Amend test to show that payload is parsed wrongly
Add 2 extra byte for >0xff length fields on adjust
Fix test for fixed length field calculation
@miri64
Copy link
Copy Markdown
Contributor Author

miri64 commented Jul 9, 2019

While working with my implementation I noticed a bug in it. I fixed it and amended the tests to detect it.

miri64 added a commit to miri64/RIOT that referenced this pull request Jul 9, 2019
I provided a this also upstream [1] but it might take a while until
there is a version of that one can install with `pip` etc. So for now
I'd like to include this into our repo to provide some tests for our
two MQTT-SN libraries, `emcute` and `asymcute`.

[1]: secdev/scapy#2131

# Layer bindings
bind_layers(UDP, MQTTSN, sport=1883)
bind_layers(UDP, MQTTSN, dport=1883)
Copy link
Copy Markdown
Member

@gpotter2 gpotter2 Jul 10, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A better way of doing this is to use 2xbind_bottom_up and one bind_layers with both port set.
This makes sure that packets have the two ports set when building, therefore UDP doesn't by default fallback on DNS or some other thing

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done and also provided a test for that case.

miri64 added a commit to miri64/RIOT that referenced this pull request Jul 12, 2019
elif val > 0xff:
return s + b"".join([chb(0x01), chb(val >> 8), chb(val & 0xff)])
else:
return s + b"".join([chb(val)])
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That looks highly inefficient

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

raise Scapy_Exception("%s: invalid length field value" %
self.__class__.__name__)
elif val > 0xff:
return s + b"".join([chb(0x01), chb(val >> 8), chb(val & 0xff)])
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

something like b"\x01" + struct.pack("!H", val) would probably be more appropriate

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Too used to C programming ^^

@gpotter2 gpotter2 merged commit be824b6 into secdev:master Jul 16, 2019
@gpotter2
Copy link
Copy Markdown
Member

Thanks for the PR !

@miri64
Copy link
Copy Markdown
Contributor Author

miri64 commented Jul 16, 2019

I did not squash yet :-/

@miri64 miri64 deleted the mqttsn_layer branch July 16, 2019 11:10
@gpotter2
Copy link
Copy Markdown
Member

It's alright, you can squash while merging a PR:
be824b6

(Though the messages will remain in the commit, but that's fine)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants