Невозможно отправить пакеты UDP на локальный порт с помощью python, не удается выполнить контрольную сумму в соответствии с WireShark

Я рвал на себе волосы из-за этого. Я пытаюсь написать сервер SOCKS5 на Python для туннелирования UDP-трафика. Я привязываюсь к порту и получаю данные нормально. Затем я анализирую заголовок SOCKS5 UDP (не типичный заголовок UDP) и перенаправляю дейтаграмму в запрошенную конечную точку.

Все хорошо. Затем я прослушиваю ответ от конечной точки (повторно отправляя, если тайм-аут) и получаю ответ. Здорово!

Вот где я схожу с ума-

Я получаю дейтаграмму от конечной точки. Я повторно инкапсулирую возвращенную дейтаграмму в соответствии с SOCKS5 RFC, который является тем же заголовком UDP, что и раньше. , за исключением того, что теперь я изменил адрес назначения и порт на исходный вызывающий абонент. Я использую:

sock_client = socket.socket(socket.AF_INET, # Internet
                            socket.SOCK_DGRAM) # UDP
sock_client.sendto(packed_datagram, (self.client_ip, self.client_port))

отправить дейтаграмму обратно клиенту. Клиент никогда не получает ответ! Всегда!

Глядя на WireShark, он говорит следующее: Контрольная сумма заголовка: 0x0000 [неверно, должно быть 0xcd1f (может быть вызвано «разгрузкой контрольной суммы IP»?)]

Разве реализация сокета Python с установленным socket.DGRAM не должна автоматически правильно упаковывать мои данные в заголовок UDP и вычислять соответствующую контрольную сумму? Почему он установлен на 0x0000? Я проверил полезную нагрузку в шестнадцатеричном формате, контрольная сумма действительно установлена ​​неправильно. Что, черт возьми, происходит?


person seibelj    schedule 04.03.2014    source источник


Ответы (4)


Расчет контрольной суммы выполняется драйверами в операционной системе. Во многих случаях расчет производится самой сетевой картой. IIRC, Wireshark захватывает локальные пакеты непосредственно перед их передачей в сетевой стек. Ошибки контрольной суммы характерны для всех локально сгенерированных пакетов.

person casevh    schedule 04.03.2014
comment
Это имеет смысл, но означает, что я действительно понятия не имею, почему локальный хост не получает мои пакеты udp. Имеет ли значение, если я отправлю их из треда? Я действительно хватаюсь за соломинку. - person seibelj; 04.03.2014

Я не точно следовал двум частям спецификации SOCKS5.

Ассоциация UDP завершается, когда разрывается TCP-соединение, по которому поступил запрос UDP
ASSOCIATE.

Я завершал сокет TCP сразу после завершения рукопожатия ретрансляции UDP. TCP должен оставаться открытым до тех пор, пока не будет завершена обратная передача.

Когда сервер ретрансляции UDP получает ответную дейтаграмму от удаленного хоста, он ДОЛЖЕН инкапсулировать эту дейтаграмму, используя указанный выше заголовок запроса UDP и любую инкапсуляцию, зависящую от метода аутентификации.

Я использовал IP-адрес и порт клиента в качестве значений в дейтаграмме. Это должен быть IP-адрес и порт удаленного сервера, по сути, инкапсуляция заголовка UDP является клоном того, что передал клиент.

После решения этих двух проблем сервер SOCKS5 работает так, как ожидалось.

person seibelj    schedule 17.03.2014

Если я правильно понимаю вашу часть кода, вы создаете новый сокет для отправки данных обратно клиенту. Таким образом, это будет новый сокет со случайным IP-адресом источника, например. с точки зрения клиента у вас есть следующий поток пакетов:

client_ip:client_port  -> socks5_ip:socks_port
client_ip:client_port  <- socks5_ip:random_port

Клиент, вероятно, имеет сокет, подключенный к серверу socks5, и поэтому ожидает ответов, поступающих от socks5_ip:socks_port, а не от random_port. Таким образом, вы не должны создавать новый сокет для клиента, а вместо этого отвечать, используя существующий сокет, в котором вы получили данные от клиента.

person Steffen Ullrich    schedule 04.03.2014
comment
Это не должно иметь значения, потому что сокеты UDP можно представить как дыры, а не каналы. Независимо от источника данных, как только данные помещаются в отверстие, они доставляются (UDP не требует установления соединения, в отличие от TCP). При этом я повторно использовал тот же сокет, на который были получены данные, как вы предложили, и все равно не повезло. По предложению коллеги я также попытался привязать сервер SOCKS5 и сервер ретрансляции UDP к моему локальному IP-адресу в сети, а не к 127.0.0.1, и настроить клиент для подключения к нему через локальный IP-адрес. Все равно не повезло :( - person seibelj; 05.03.2014
comment
Пожалуйста, предоставьте tcpdump соединения между клиентом и сервером socks. - person Steffen Ullrich; 05.03.2014
comment
Спасибо за ваше время, я решил свою проблему в ответе на вопрос. - person seibelj; 17.03.2014

отключите контрольную сумму tx с помощью команды linux:

ethtool -K eth0 tx off

ИЛИ используйте эту функцию для вычисления контрольной суммы

def checksum(data):
    s = 0
    n = len(data) % 2
    for i in range(0, len(data)-n, 2):
        s+= ord(data[i]) + (ord(data[i+1]) << 8)
    if n:
        s+= ord(data[i+1])
    while (s >> 16):
        s = (s & 0xFFFF) + (s >> 16)
    s = ~s & 0xffff
    return s

Где данные являются псевдозаголовком

person Th3carpenter    schedule 29.11.2014