GNU/Linux >> Znalost Linux >  >> Linux

Linux:Svázat naslouchací soket UDP s konkrétním rozhraním (nebo zjistit rozhraní, ze kterého přišel datagram)?

Řešení, které jsem zjistil, že funguje, je následující. Nejprve musíme změnit nastavení ARP a RP. Do /etc/sysctl.conf přidejte následující a restartujte počítač (existuje také příkaz pro dynamické nastavení):

net.ipv4.conf.default.arp_filter = 1
net.ipv4.conf.default.rp_filter = 2
net.ipv4.conf.all.arp_filter = 1
net.ipv4.conf.all.rp_filter = 2

Filtr arp byl nezbytný, aby umožnil směrování odpovědí z eth0 přes WAN. Volba filtru rp byla nezbytná pro striktní přiřazení příchozích paketů k síťové kartě, na které přišly (na rozdíl od slabého modelu, který je spojuje s jakoukoli síťovou kartou odpovídající podsíti). K tomuto kritickému kroku mě přivedl komentář od EJP.

Poté začal SO_BINDTODEVICE pracovat. Každý ze dvou soketů byl vázán na svou vlastní síťovou kartu, a proto jsem mohl určit, ze které síťové karty přišla zpráva, podle soketu, ze kterého přišla.

s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
rc=setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, nic, IF_NAMESIZE);
memset((char *) &si_me, 0, sizeof(si_me));
si_me.sin_family = AF_INET;
si_me.sin_port = htons(LISTEN_PORT);
si_me.sin_addr.s_addr = htonl(INADDR_ANY);
rc=bind(s, (struct sockaddr *)&si_me, sizeof(si_me))

Dále jsem chtěl reagovat na příchozí datagramy datagramy, jejichž zdrojová adresa je adresa NIC, ze které pochází původní požadavek. Odpovědí je pouze vyhledat adresu NIC a svázat odchozí soket s touto adresou (pomocí bind ).

s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
get_nic_addr(nics, (struct sockaddr *)&sa)
sa.sin_port = 0;
rc = bind(s, (struct sockaddr *)&sa, sizeof(struct sockaddr));
sendto(s, ...);

int get_nic_addr(const char *nic, struct sockaddr *sa)
{
    struct ifreq ifr;
    int fd, r;
    fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd < 0) return -1;
    ifr.ifr_addr.sa_family = AF_INET;
    strncpy(ifr.ifr_name, nic, IFNAMSIZ);
    r = ioctl(fd, SIOCGIFADDR, &ifr);
    if (r < 0) { ... }
    close(fd);
    *sa = *(struct sockaddr *)&ifr.ifr_addr;
    return 0;
}

(Možná, že vyhledávání adresy NIC pokaždé vypadá jako plýtvání, ale je to mnohem více kódu, abyste byli informováni, když se adresa změnila, a tyto transakce probíhají pouze jednou za několik sekund v systému, který nefunguje na baterii.)


Cílovou adresu, kterou používá odesílatel, můžete získat pomocí IP_RECVDSTADDR pokud to vaše platforma podporuje, pomocí recvmsg() . Je to poměrně komplikované, popsané v Programování sítí Unix svazek I, 3. vydání, #22.2, a v man stránku.

Při vaší úpravě stojíte proti tomu, co je známé jako „model slabého konce systému“ TCP/IP. V zásadě, jakmile paket dorazí, systém se může rozhodnout jej doručit přes jakékoli vhodné rozhraní naslouchající správnému portu. Někde je to popsáno v TCP/IP RFC.


Do setsockopt předáváte neplatnou hodnotu .

rc=setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, nic, strlen(nic));

Manová stránka říká SO_BIND_TO_DEVICE :

Předaná možnost je ukončená s proměnnou délkou řetězec názvu rozhraní s maximální velikostí IFNAMSIZ

strlen nezahrnuje ukončovací null. Můžete zkusit:

rc=setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, nic, 1 + strlen(nic));

dnsmasq funguje správně a používá

setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, intname, IF_NAMESIZE)

Linux
  1. Jak najít balíček, který poskytuje konkrétní soubor v Linuxu

  2. Linux:Zjistěte, kdo je přihlášen k serveru

  3. jak navázat raw socket na konkrétní rozhraní

  1. Jak najdu konkrétní soubor z terminálu Linux?

  2. Jak mohu odeslat zprávu na můj socket.io websocket z příkazového řádku v linuxu?

  3. Jak zjistit, jaké rozhraní používám pro připojení k internetu?

  1. Linux – Jak zjistit, jaké pevné disky jsou v systému?

  2. Jak zjistit velikost vyrovnávací paměti soketu linuxu

  3. Linux:zjistěte, jaký proces využívá všechnu RAM?