Как ограничить трафик с помощью многоадресной рассылки по локальному хосту - PullRequest
7 голосов
/ 21 мая 2010

Я использую многоадресный UDP через localhost, чтобы реализовать свободную коллекцию совместных программ, работающих на одной машине.Следующий код хорошо работает на Mac OSX, Windows и Linux.Недостатком является то, что код будет принимать пакеты UDP и за пределами локальной сети.Например, sendSock.sendto(pkt, ('192.168.0.25', 1600)) получено моей тестовой машиной при отправке из другого ящика в моей сети.

import platform, time, socket, select

addr = ("239.255.2.9", 1600)

sendSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sendSock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 24)
sendSock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, 
    socket.inet_aton("127.0.0.1"))

recvSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
recvSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
if hasattr(socket, 'SO_REUSEPORT'):
    recvSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, True)

recvSock.bind(("0.0.0.0", addr[1]))
status = recvSock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, 
    socket.inet_aton(addr[0]) + socket.inet_aton("127.0.0.1"));

while 1:
    pkt = "Hello host: {1} time: {0}".format(time.ctime(), platform.node())
    print "SEND to: {0} data: {1}".format(addr, pkt)
    r = sendSock.sendto(pkt, addr)

    while select.select([recvSock], [], [], 0)[0]:
        data, fromAddr = recvSock.recvfrom(1024)
        print "RECV from: {0} data: {1}".format(fromAddr, data)

    time.sleep(2)

Я попытался recvSock.bind(("127.0.0.1", addr[1])), но это не позволяет сокету получать многоадресный трафик.Есть ли правильный способ настроить recvSock для приема только многоадресных пакетов из сети 127/24, или мне нужно проверить адрес каждого полученного пакета?

Ответы [ 4 ]

5 голосов
/ 21 февраля 2013

Вопреки тому, что было сказано в других ответах, IPv4 поддерживает многоадресную область на основе TTL следующим образом:

0: node-local (not forwarded outside the current host)
1: link-local (not forwarded outside the current subnet)
< 32: site-local
< 64: region-local
< 128: continent-local
< 255: global

(также поддерживается Многоадресная рассылка с административной областью .)

Источник: У. Р. Стивенс, Сетевое программирование Unix, 2 nd издание, том I, раздел 19.2, с исправлениями, соответствующими RFC 2365 .

2 голосов
/ 21 мая 2010

К сожалению, многоадресный IP не имеет такой функции «фильтрации по подсети» - поэтому, если вы не хотите использовать IPTables (в Linux) или эквивалентный «межсетевой экран» SW / HW вашей системы / сети, чтобы попробовать «бросьте слово» каждому многоадресному пакету, который вам не нравится, я думаю, вам придется делать это на уровне приложения (например, с проверкой на fromAddr во внутреннем цикле). Является ли трафик IP с других хостов настолько сильным, что это снижает вашу производительность ...?

1 голос
/ 17 марта 2011

Если ваш хост поддерживает IPv6, вы можете использовать компонент области адреса многоадресной рассылки (это «x» в префиксе многоадресной рассылки FF0x :), чтобы неявно ограничить как входящие, так и исходящие пакеты локальным хостом, указав область из 1 (например, используйте адрес многоадресной рассылки IPv6 FF01 :: 107 для «сервера службы имен» только на локальном хосте). К сожалению, механизм многоадресной рассылки IPv4 не имеет явной области действия, а RFC 2365 (http://tools.ietf.org/html/rfc2365), который определяет административные области многоадресной рассылки IPv4, не определяет локальный адрес области действия узла, только диапазон области локальной связи.

0 голосов
/ 22 мая 2010

Вы можете использовать от connect() до 127.0.0.1 на сокете многоадресной рассылки, тогда стек IP может фильтровать для вас.

Обновлено с исходным кодом для демонстрации

Вы можете запустить этот скрипт несколько раз на одном хосте и посмотреть, как распределяются многоадресные пакеты:

#!/usr/bin/python

import platform, time, socket, select

addr = ("239.255.2.9", 1600)

sendSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sendSock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 24)
sendSock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF,
    socket.inet_aton("127.0.0.1"))

recvSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
recvSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
if hasattr(socket, 'SO_REUSEPORT'):
    recvSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, True)

recvSock.bind(("0.0.0.0", addr[1]))
status = recvSock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP,
    socket.inet_aton(addr[0]) + socket.inet_aton("127.0.0.1"));

sendSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
if hasattr(socket, 'SO_REUSEPORT'):
    sendSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, True)
sendSock.bind(("127.0.0.1", addr[1]));
recvSock.connect(("127.0.0.1", addr[1]));

while 1:
    pkt = "Hello host: {1} time: {0}".format(time.ctime(), platform.node())
    print "SEND to: {0} data: {1}".format(addr, pkt)
    r = sendSock.sendto(pkt, addr)

    while select.select([recvSock], [], [], 0)[0]:
        data, fromAddr = recvSock.recvfrom(1024)
        print "RECV from: {0} data: {1}".format(fromAddr, data)

    time.sleep(2)

Чтобы инициировать пакеты с другого интерфейса:

#!/usr/bin/python

import platform, time, socket, select

sendSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)

pkt = "Hello from network.";
sendSock.sendto(pkt, ('10.65.42.129', 1600))

Я запустил все три на Cygwin в Windows XP и убедился, что результат соответствует требованиям.

Пример вывода

SEND to: ('239.255.2.9', 1600) data: Hello host: NYCMDV8X5R1 time: Thu Feb 21 13:15:15 2013
RECV from: ('127.0.0.1', 1600) data: Hello host: NYCMDV8X5R1 time: Thu Feb 21 13:15:14 2013
RECV from: ('127.0.0.1', 1600) data: Hello host: NYCMDV8X5R1 time: Thu Feb 21 13:15:15 2013
SEND to: ('239.255.2.9', 1600) data: Hello host: NYCMDV8X5R1 time: Thu Feb 21 13:15:17 2013
RECV from: ('127.0.0.1', 1600) data: Hello host: NYCMDV8X5R1 time: Thu Feb 21 13:15:16 2013
RECV from: ('127.0.0.1', 1600) data: Hello host: NYCMDV8X5R1 time: Thu Feb 21 13:15:17 2013

Ранее в выходных данных отображались внешние пакеты, например:

SEND to: ('239.255.2.9', 1600) data: Hello host: NYCMDV8X5R1 time: Thu Feb 21 13:07:05 2013
RECV from: ('10.65.42.129', 4711) data: Hello from network.
RECV from: ('127.0.0.1', 4710) data: Hello host: NYCMDV8X5R1 time: Thu Feb 21 13:07:05 2013
SEND to: ('239.255.2.9', 1600) data: Hello host: NYCMDV8X5R1 time: Thu Feb 21 13:07:11 2013
RECV from: ('10.65.42.129', 4712) data: Hello from network.
...