From c014c607cc49a84200c2ced3efb2d9c63ec9f439 Mon Sep 17 00:00:00 2001 From: gpotter2 <10530980+gpotter2@users.noreply.github.com> Date: Thu, 29 May 2025 18:03:27 +0200 Subject: [PATCH] BGP: support tcp_reassemble --- doc/scapy/usage.rst | 2 ++ scapy/contrib/bgp.py | 26 ++++++++++++++++++++++---- test/contrib/bgp.uts | 7 +++++++ test/pcaps/bgp_fragmented.pcap.gz | Bin 0 -> 1173 bytes 4 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 test/pcaps/bgp_fragmented.pcap.gz diff --git a/doc/scapy/usage.rst b/doc/scapy/usage.rst index a6e14a84fcc..0a5bb16da2b 100644 --- a/doc/scapy/usage.rst +++ b/doc/scapy/usage.rst @@ -847,6 +847,8 @@ The ``data`` argument is bytes and the ``metadata`` argument is a dictionary whi - ``metadata.get("tcp_psh", False)``: will be present if the PUSH flag is set - ``metadata.get("tcp_end", False)``: will be present if the END or RESET flag is set +If ``tcp_reassemble`` **returns any padding**, it will be kept for the next payload. + Filters ------- diff --git a/scapy/contrib/bgp.py b/scapy/contrib/bgp.py index d8564a9b795..0060e5f9f32 100644 --- a/scapy/contrib/bgp.py +++ b/scapy/contrib/bgp.py @@ -461,6 +461,17 @@ def dispatch_hook(cls, _pkt=None, *args, **kargs): return _bgp_dispatcher(_pkt) + @classmethod + def tcp_reassemble(cls, data, *args, **kwargs): + if len(data) < 18: + return None + if data[:16] == _BGP_HEADER_MARKER: + length = struct.unpack("!H", data[16:18])[0] + if len(data) >= length: + return cls(data[:length]) / conf.padding_layer(data[length:]) + else: + return cls(data) + def post_build(self, p, pay): if self.len is None: length = len(p) @@ -520,6 +531,8 @@ def dispatch_hook(cls, _pkt=None, *args, **kargs): return _bgp_dispatcher(_pkt) + tcp_reassemble = BGPHeader.tcp_reassemble + def guess_payload_class(self, p): cls = None if len(p) > 15 and p[:16] == _BGP_HEADER_MARKER: @@ -755,7 +768,8 @@ class ORFTuple(Packet): "entries", [], ORFTuple, - count_from=lambda p: p.orf_number + count_from=lambda p: p.orf_number, + max_count=20000, ) ] @@ -811,7 +825,8 @@ class BGPCapORF(BGPCapability): "orf", [], BGPCapORFBlock, - length_from=lambda p: p.length + length_from=lambda p: p.length, + max_count=20000, ) ] @@ -1910,7 +1925,8 @@ class BGPPAMPReachNLRI(Packet): ConditionalField(IP6Field("nh_v6_link_local", "::"), lambda x: x.afi == 2 and x.nh_addr_len == 32), ByteField("reserved", 0), - MPReachNLRIPacketListField("nlri", [], BGPNLRI_IPv6)] + MPReachNLRIPacketListField("nlri", [], BGPNLRI_IPv6, + max_count=20000)] def post_build(self, p, pay): if self.nlri is None: @@ -2216,7 +2232,8 @@ class BGPUpdate(BGP): BGPPathAttr, length_from=lambda p: p.path_attr_len ), - BGPNLRIPacketListField("nlri", [], "IPv4") + BGPNLRIPacketListField("nlri", [], "IPv4", + max_count=20000) ] def post_build(self, p, pay): @@ -2574,6 +2591,7 @@ class BGPORF(Packet): [], Packet, length_from=lambda p: p.orf_len, + max_count=20000, ) ] diff --git a/test/contrib/bgp.uts b/test/contrib/bgp.uts index cebb1bfafe4..3d8e30f2431 100644 --- a/test/contrib/bgp.uts +++ b/test/contrib/bgp.uts @@ -87,6 +87,13 @@ h = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x assert h.type == BGP.OPEN_TYPE assert h.len == 19 += BGP - Test TCP reassembly +pkts = sniff(offline=scapy_path("/test/pcaps/bgp_fragmented.pcap.gz"), session=TCPSession) +assert len(pkts) == 1 +assert BGPUpdate in pkts[0] +assert len(pkts[0].nlri) == 512 +assert pkts[0].nlri[511].prefix == '91.0.177.0/24' + ############################### BGPKeepAlive ################################# + BGPKeepAlive class tests diff --git a/test/pcaps/bgp_fragmented.pcap.gz b/test/pcaps/bgp_fragmented.pcap.gz new file mode 100644 index 0000000000000000000000000000000000000000..71d84784e31bd36aad87cd5e4b1f72885cb0cab0 GIT binary patch literal 1173 zcmV;G1Zw*qiwFo7gg9sb17c@zUuJS)XKiI}bY)~NaARR`0A1BpkX%(11<5>^*pc{-+N)^5MQC{`cyfrNi%x*PT>%N3RY) zI((e&k$SYR>*`k{F$SYCI-QJ32VrE!Vge>)Y;<1ilMGb*Fd#Y9ZY6imz{Ov>a; z##Bth)XdH-%*t%c!A#7^%*?>_EWn)1&wR|wluXAmEXC3+!eT7S!YstHbh9eUu?j1* zBz;(kb`}e=1dB5db2AqkvJvaD4r{Uo8?zppusX}L8f&u_E3yKc(x1)PoNd^FE!dHr z=tWO@urvMGj&0eRt=N+F*??WyjomqrgV=|I*_Zv z1~QnFIGJ-el~Xv4qdA-tIf2tTmSZ@ILpY2>Ih#v3pG&!n%ejz?IE(8!gY&qKYq^Fq zxqyo~m)p3tt6yXw&MdMQnMupe&D`X(>_omIqiK1(#k(BjIiAIx#@*&gp5SpF;$h?` z?mzB7vKDtAd5SFMZQkH5-sD5fM9fRfMTYS?Utpf{Dsr1o_?VA8%Zt3=^P|XG9>clB z|D7LsiJ|`H8}H3)ypF8rYu@91KJYgq_|<3U7V{8ilb111F+(vMk(a!JOr~YFJ>MP3 zXC6Tw;vQqxV!rYY&(ogKvv!pC2Y8SnxW~AQ+{iuL%iXx6xWl;5xYwA4n2+4aU9>wh zhk1&pd4}7$f_6Xc-rQe4<5T1#auRnQ`HB2R79uN=xyW4PEV3QBj_gL}@-A1>&PDzr zfB6e_4#oM&_e0as&5qKk^;SU-*fiF&8lxF()xE z`JV5%8X1l(N2YTXfAR;vGrX%c{}9dphAhK_cQ){e%Hc8|7CYkH?p z{6k8)LIkre_mDnn2+d-16O^}TeO%;t7Z5OQ=+Kv`3l#(l(`eN@_}aO6!zFmjqTz zvWY1ZQ`(`VyEI5?iP93)T0;AjsVEIpt$|EIX^GMbrLoF1mT4?gUz)JAWNFE2Eo53t nOO}=_(_SXMOmk_D(w1d<%e0kbmuW5&R5$(&_WDWu=Li4*K$BSx literal 0 HcmV?d00001