Обход некоторых уровней пакетов в Python Scapy

Я хочу создать IP-пакет с частью Vlan. Это легко сделать с помощью Scapy:

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

Иногда я хочу иметь inet.Dot1Q() в пакете, иногда нет. Каким должно быть значение по умолчанию для inet.Dot1Q(), чтобы его можно было просто обойти в операторе '/'? Пробовал '' и None - не работают.

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

EDIT: другое объяснение моей проблемы

1) Я могу создать пакет с тегом VLAN

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

2) Я могу создать пакет с двойным тегом VLAN

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

3) Как создать пакет, который может быть нетегированным пакетом, пакетом с тегом VLAN или пакетом с двойным тегом VLAN? Было бы здорово иметь что-то вроде:

#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()

Я не могу понять, каким должно быть значение по умолчанию ???, чтобы можно было правильно построить пакет.


person Konstantin    schedule 13.04.2014    source источник
comment
Все еще ищу хорошее решение!   -  person Konstantin    schedule 18.12.2014


Ответы (2)


Прежде всего, я просто не могу придумать ни одной причины, по которой вы бы не использовали такое простое решение:

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

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

Но в любом случае, если этот подход по какой-либо причине неприемлем, есть другая возможность...
Вы можете переопределить класс Packet и создать свой собственный класс, нейтральный по отношению к оператору деления. Единственное, что вам нужно, это переопределить метод __div__ и добавить несколько круглых скобок, чтобы сохранить правильный приоритет.

Вот как бы вы это реализовали:

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()))

Дополнительное объяснение:

Метод __div__() является реализацией подразделения operator. Интерпретатор выполняет a/b как a.__div__(b), а класс Packet имеет собственную реализацию этого оператора. Если вы посмотрите на его код, вы увидите, что в основном это добавление payload пакета b к пакету a.
Вместо этого мы просто возвращаем пакет other, в данном случае b, полностью игнорируя a.
Обратите внимание, что это работает только в одном направлении, когда NeutralPacket является левосторонним оператором, потому что в противном случае выполняется метод __div__() какого-то другого класса. Чтобы справиться с этим, мы должны добавить круглые скобки, чтобы приоритет делал наш класс NeutralPacket всегда левым оператором. И поскольку это не влияет на общий результат из-за реализации Packet, это хорошо работает для вашего случая. Единственное, что не сработало бы, это если бы NeutralPacket был бы самым правым (то есть последним) оператором, поскольку тогда вы не могли бы установить правильный приоритет. Но для VLAN это не проблема, так как над ним всегда есть уровни.

person bosnjak    schedule 25.12.2014

Я совсем забыл о Raw().

Слой Raw — это то, что вам нужно.

Raw(), если туда не помещены данные, ничего не добавит к созданному вами пакету. Вы можете использовать это как значение по умолчанию для вашей переменной myDot1Q.

>>> 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
person RyPeck    schedule 14.04.2014
comment
Проблема в том, что я хочу иметь общий scapy-пакет, например inet.Ether()/firstDot1Q/secondDot1Q/thirdDot1Q/inet.IP(). Иногда мне нужен двойной и т. д. VLAN, иногда нет. - person Konstantin; 14.04.2014
comment
Что вы подразумеваете под общим пакетом Scapy? - person RyPeck; 14.04.2014
comment
Я имел в виду такую ​​конструкцию: inet.Ether()/firstDot1Q/secondDot1Q/thirdDot1Q/inet.IP(). Вышеупомянутая строка всегда одна и та же, однако я бы хотел, чтобы ThirdDot1Q = VOID или WHATEVER, чтобы она стала невидимой, и пакет был построен правильно без учета этой части. - person Konstantin; 14.04.2014
comment
У вас нет проблем с созданием пакетов scapy, просто немного некрасиво создавать все пакеты с нуля без возможности их повторного использования, как я упоминал ранее. - person Konstantin; 14.04.2014
comment
Если это последнее редактирование не отвечает на ваш вопрос, опубликуйте более подробную информацию о том, чего вы пытаетесь достичь. - person RyPeck; 14.04.2014
comment
Спасибо за помощь, немного по другому объяснил вопрос - person Konstantin; 15.04.2014
comment
Понятно - Raw() должен делать то, что вы хотите. - person RyPeck; 15.04.2014
comment
Привет! Я попробовал ваше решение, но оно не работает, как я ожидал. Например: b = inet.Ether()/inet.Raw()/inet.Dot1Q()/inet.IP() - эта строка работает некорректно. Ether() и Dot1Q() не объединяются должным образом (как будто между ними нет Raw()) - person Konstantin; 20.04.2014
comment
Что случается? - Обновите свой ответ. - person RyPeck; 21.04.2014
comment
Кроме того, вы должны импортировать Scapy с from scapy import * - person RyPeck; 21.04.2014