Question

I want to construct an IP packet with Vlan part. It's easily done with Scapy:

from scapy import inet
newpkt = inet.Ether()/inet.Dot1Q()/inet.IP()

Sometimes I want to have inet.Dot1Q() in the packet, sometimes no. What should be the default value for inet.Dot1Q() so it's simply bypassed in the operator '/'? I tried '' and None - they don't work.

from scapy import inet
myDot1Q = SOME DEFAULT VALUE
newpkt = inet.Ether()/myDot1Q/inet.IP()
#new packet is a valid IP packet here

EDIT: A different explanation of my problem

1) I can create a packet with VLAN tag

inet.Ether()/inet.Dot1Q/inet.IP()

2) I can create a packet with double VLAN tag

inet.Ether()/inet.Dot1Q/inet.Dot1Q/inet.IP()

3) How can I build a packet that may be an untagged packet, VLAN tagged packet or double VLAN tagged packet? It would be great to have something like:

#No VLAN
myVlan = ???
myDoubleVlan = ???

#VLAN
myVlan = inet.Dot1Q()
myDoubleVlan = ???

#Double VLAN
myVlan = inet.Dot1Q()
myDoubleVlan = inet.Dot1Q()

#In any case the packet structure should remain the same
inet.Ether()/myVlan/myDoubleVlan/inet.IP()

I can't figure out what the default value ??? should be to be able to properly construct the packet.

Was it helpful?

Solution

First of all, I just can't think of any reason why you wouldn't use a simple solution like this:

# example for single Vlan
# myVlan = Dot1Q()
# myDoubleVlan = None

# generate the package
package = Ether()
if myVlan: package /= myVlan
if myDoubleVlan: package /= myDoubleVlan
package /= IP()

But anyway, if this approach is for whatever reason not acceptable, there is another possibility...
You could override the Packet class and create your own class that is neutral to the division operator. The only thing you need is to override the __div__ method and to add some parentheses to preserve the right precedence.

This is how you would implement it:

from scapy.all import *

class NeutralPacket(Packet):
    def __init__(self,  _pkt="", _internal=0, **fields):
        super(NeutralPacket, self).__init__(_pkt, _internal, **fields)
    def __div__(self, other):
        return other

#No VLAN
myVlan = NeutralPacket()
myDoubleVlan = NeutralPacket()

#VLAN
myVlan = Dot1Q()
myDoubleVlan = NeutralPacket()

#Double VLAN
myVlan = Dot1Q()
myDoubleVlan = Dot1Q()

# this part doesn't need to change, but you need to have two
# additional parenthesis for proper precedence
Ether()/(myVlan/(myDoubleVlan/IP()))

Additional explanation:

The __div__() method is the division operator implementation. The interpreter executes a/b as a.__div__(b), and the Packet class has its own implementation of this operator. If you take a look at its code, you can see that its basically appending the payload of package b to package a.
What we are doing here instead is just returning the other package, being b in this case, effectively ignoring a completely.
Note that this works only in one direction, when NeutralPacket is the left-hand-side operator, because otherwise it's some other's class' __div__() method being executed. To handle this, we have to add parenthesis so that the precedence will make our NeutralPacket class always be the left-hand-side operator. And since this doesn't effect the overall result due to Packet's implementation, this works nicely for your case. The only thing that wouldn't work is if the NeutralPacket would be the most right-hand-side (i.e. the last) operator, since then you couldn't force the right precedence. But for VLAN this is not an issue, since there are always layers above it..

OTHER TIPS

I had completely forgotten about Raw().

The Raw layer is what you want.

Raw(), if no data is placed there, will add nothing to your constructed Packet. You can use this as a default value for your myDot1Q variable.

>>> b = Raw() / ICMP()
>>> a = Raw() / ICMP()
>>> b = ICMP()
>>> a.show()
###[ Raw ]###
  load= ''
###[ ICMP ]###
     type= echo-request
     code= 0
     chksum= None
     id= 0x0
     seq= 0x0
>>> b.show()
###[ ICMP ]###
  type= echo-request
  code= 0
  chksum= None
  id= 0x0
  seq= 0x0
>>> a.build()
'\x08\x00\xf7\xff\x00\x00\x00\x00'
>>> b.build()
'\x08\x00\xf7\xff\x00\x00\x00\x00'
>>> a.build() == b.build()
True
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top