Reputation: 3159
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.
Upvotes: 3
Views: 2186
Reputation: 8614
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()))
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..
Upvotes: 4
Reputation: 8147
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
Upvotes: 1